package mk.com.ldm.corepoint.implementation.client.lib.custom.cpstable;

import java.util.ArrayList;
import java.util.HashMap;

import mk.com.ldm.corepoint.implementation.client.CoreServiceAsync;
import mk.com.ldm.corepoint.implementation.client.lib.Constants.CPConstants;
import mk.com.ldm.corepoint.implementation.client.lib.generators.FilterFactory;
import mk.com.ldm.corepoint.implementation.client.presenter.EditPresenter;
import mk.com.ldm.corepoint.implementation.client.view.EditView;
import mk.com.ldm.corepoint.implementation.client.view.ModalOverlay;
import mk.com.ldm.corepoint.implementation.shared.DataBaseObject;
import mk.com.ldm.corepoint.implementation.shared.Filter;
import mk.com.ldm.corepoint.implementation.shared.TableColumn;
import mk.com.ldm.corepoint.implementation.shared.TableData;
import mk.com.ldm.corepoint.implementation.shared.TableRow;
import mk.com.ldm.corepoint.implementation.shared.TableRows;

import org.gwt.mosaic.ui.client.MessageBox;
import org.gwt.mosaic.ui.client.table.AbstractScrollTable.ColumnResizePolicy;
import org.gwt.mosaic.ui.client.table.FixedWidthFlexTable;
import org.gwt.mosaic.ui.client.table.FixedWidthGrid;
import org.gwt.mosaic.ui.client.table.ScrollTable;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

@SuppressWarnings("deprecation")
public class CustomPagingScrollTable extends Composite implements HasHandlers {
	private int pageSize = 10;
	private int currentPage = 1;
	private int totalPages = 1;
	private Button next = new Button(CPConstants.NEXT_IMAGE);
	private Button prev = new Button(CPConstants.PREVIOUS_IMAGE);
	private Button last = new Button(CPConstants.LAST_IMAGE);
	private Button first = new Button(CPConstants.FIRST_IMAGE);
	private ScrollTable table;
	private HandlerManager handlerManager;
	private ActionEvent actionEvent = new ActionEvent();
	private TextBox currPageTxt = new TextBox(), pageSizeTxt = new TextBox();
	public static final int EDIT = 0, DELETE = 1, BOTH = 2, READ_ONLY = 3,
			SELECT = 4, REPORT = 6;
	private int type = DELETE, headerCell = 1;
	private String direction = " DESC", order = "";
	private ArrayList<DataBaseObject> columns = new ArrayList<DataBaseObject>();
	private CoreServiceAsync rpcService;
	private String username = "", objectName = "", objectType = "", name = "";
	private boolean initialUse = false;
	private TableRows tableRows = new TableRows();
	private Label totalPagesLbl = new Label();
	private String searchString = "";
	private FlexTable filterPanel = new FlexTable();
	private HashMap<String, Filter> filters = new HashMap<String, Filter>();


	private Button filterBtn = new Button(CPConstants.SEARCH_IMAGE);
	private String dataBaseTableName = new String();
	private Label errorMessage = new Label("");
	private HashMap<String, Boolean> reportVisibleColumns = new HashMap<String, Boolean>();

	public CustomPagingScrollTable() {
		FixedWidthFlexTable headerTable = new FixedWidthFlexTable();
		FixedWidthGrid dataTable = new FixedWidthGrid();
		ScrollTable table = new ScrollTable(dataTable, headerTable);
		table.addStyleName("ui-ScrollTable");
		table.setWidth("960px");
		table.setHeight((Window.getClientHeight() - 140) + "px");
		filterPanel.setWidth(table.getOffsetWidth() + "px");
		VerticalPanel vp = new VerticalPanel();
		errorMessage.setStyleName("list-error");
		vp.add(errorMessage);
		vp.add(filterPanel);
		this.table = table;
		vp.add(table);
		init();
		initWidget(vp);
	}
	
	public HashMap<String, Filter> getFilters() {
		return filters;
	}

	public void setFilters(HashMap<String, Filter> filters) {
		this.filters = filters;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getCurrentPage() {
		return currentPage;
	}

	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}

	public int getTotalPages() {
		return totalPages;
	}

	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public CoreServiceAsync getRpcService() {
		return rpcService;
	}

	public void setRpcService(CoreServiceAsync rpcService) {
		this.rpcService = rpcService;
	}

	public HandlerRegistration addActionEventHandler(ActionEventHandler handler) {
		return handlerManager.addHandler(ActionEvent.TYPE, handler);
	}

	public void fireEvent(GwtEvent<?> event) {
		handlerManager.fireEvent(event);
	}

	public String getOrder() {
		return order;
	}

	public void setOrder(String order) {
		this.order = order;
	}

	public ArrayList<DataBaseObject> getColumns() {
		return columns;
	}

	public void setColumns(ArrayList<DataBaseObject> columns) {
		this.columns = columns;
	}

	public boolean isInitialUse() {
		return initialUse;
	}

	public void setInitialUse(boolean initialUse) {
		this.initialUse = initialUse;
	}

	public TableRows getTableRows() {
		return tableRows;
	}

	public void setTableRows(TableRows tableRows) {
		this.tableRows = tableRows;
	}

	public ScrollTable getTable() {
		return table;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getObjectName() {
		return objectName;
	}

	public void setObjectName(String objectName) {
		this.objectName = objectName;
	}

	public String getObjectType() {
		return objectType;
	}

	public void setObjectType(String objectType) {
		this.objectType = objectType;
	}

	public String getSearchString() {
		return searchString;
	}

	public void setSearchString(String searchString) {
		this.searchString = searchString;
	}
	
	
	public void addFilter(Filter filter, String name){
		filters.put(name,filter);
	}
	
	public void removeFilter(String name){
		filters.remove(name);
	}
	
	public HashMap<String, Boolean> getReportVisibleColumns(){
		return reportVisibleColumns;
	}

	private void addControls(int row, TableData td, TableRow tr) {
		final FixedWidthGrid dataTable = table.getDataTable();
		int cells = dataTable.getCellCount(0) - 1;
		if (type != READ_ONLY && type != REPORT) {
			String id = td.getValue();

			if (type == DELETE || type == BOTH) {
				CheckBox cb = new CheckBox();
				cb.setName(id);
				dataTable.setWidget(row, 0, cb);
				dataTable.getCellFormatter().setAlignment(row, 0,
						HasHorizontalAlignment.ALIGN_CENTER,
						HasVerticalAlignment.ALIGN_MIDDLE);
				HTML h = new HTML("&nbsp;");
				h.setWidth("38px");
				filterPanel.setWidget(0, 0, h);
			}
			HorizontalPanel hp = new HorizontalPanel();
			if (type == EDIT || type == BOTH)
				hp.add(getEditButton(id));
			if (type == DELETE || type == BOTH)
				hp.add(getDeleteButton(id));
			if (type == SELECT)
				hp.add(getSelectButton(tr));
			dataTable.setWidget(row, cells, hp);
		}
	}
	
	public void addFilters(){
		if (initialUse) {
			int i = (type == DELETE || type == BOTH) ? 1 : 0;
			boolean hasFilters = false;
			int row = type==REPORT?1:0;
			for (final TableColumn tc : tableRows.getColumns()) {
				// filters
				if (tc.isFilter()) {
					filterPanel.setWidget(row, i, new FilterFactory(filters).getWidget(tc,
							filterBtn, rpcService));
					hasFilters = true;
				}
				else{
					if(tc.isVisible()){
						HTML h = new HTML("&nbsp;");
						h.setWidth((tc.getWidth()+6)+"px");
						filterPanel.setWidget(row, i, h);
					}
				}
				if(type==REPORT){
					reportVisibleColumns.put(tc.getHeader(), true);
					
					HorizontalPanel hp = new HorizontalPanel();
					hp.add(new HTML(tc.getHeader()));
					
					CheckBox cb = new CheckBox();
					cb.setValue(true);
					cb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
						@Override
						public void onValueChange(ValueChangeEvent<Boolean> event) {
							reportVisibleColumns.put(tc.getHeader(), event.getValue());
						}
					});
					hp.add(cb);
					filterPanel.setWidget(0, i, hp);
				}
				i++;
			}
			if (hasFilters) {
				filterBtn.addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						fillTable();
					}
				});
				filterPanel.setWidget(row, i,filterBtn);
				filterBtn.addStyleName("filter-button");
			}
			initialUse = false;
		}
	}

	public void addHeaderAndFooter() {
		FlexTable footer = new FlexTable();

		table.setColumnResizePolicy(ColumnResizePolicy.DISABLED);

		totalPagesLbl.setText(" of " + totalPages);
		if (initialUse) {
			filterPanel.addStyleName("filter-table");
			int cells = table.getDataTable().getCellCount(0);

			FixedWidthFlexTable headerTable = table.getHeaderTable();
			
			int i = (type == DELETE || type == BOTH) ? 1 : 0;
			for (final TableColumn tc : tableRows.getColumns()) {
				headerTable.setHTML(0, i, tc.getHeader());
				headerTable.getFlexCellFormatter().setHorizontalAlignment(0, i,
						HasHorizontalAlignment.ALIGN_CENTER);
				headerTable.getFlexCellFormatter().setStyleName(0, i,
						"cursor_pointer");
				i++;
			}

			if ((type == DELETE || type == BOTH) && type != READ_ONLY) {
				CheckBox selectAll = new CheckBox();
				headerTable.setWidget(0, 0, selectAll);
				headerTable.getCellFormatter().setAlignment(0, 0,
						HasHorizontalAlignment.ALIGN_CENTER,
						HasVerticalAlignment.ALIGN_MIDDLE);
				headerTable.getCellFormatter().setStyleName(0, 0,
						"cursor_default");
				table.setColumnSortable(0, false);

				selectAll
						.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
							@Override
							public void onValueChange(
									ValueChangeEvent<Boolean> event) {
								for (int i = 0; i < table.getDataTable()
										.getRowCount(); i++) {
									((CheckBox) table.getDataTable().getWidget(
											i, 0)).setValue(event.getValue());
								}
							}
						});
			}

			if (type != READ_ONLY && type!=REPORT) {
				headerTable.setText(0, cells - 1, "Controls");
				table.setColumnSortable(cells - 1, false);
				headerTable.getCellFormatter().setHorizontalAlignment(0,
						cells - 1, HasHorizontalAlignment.ALIGN_CENTER);
				headerTable.getCellFormatter().setStyleName(0, cells - 1,
						"cursor_default");
				if (type == DELETE || type == BOTH)
					table.setMaximumColumnWidth(0, 30);
			}

			if(type != REPORT){
				VerticalPanel pageInfo = new VerticalPanel();
				HorizontalPanel pageControls = new HorizontalPanel();
				currPageTxt.setWidth("40px");
				currPageTxt.setText(Integer.toString(currentPage));
				pageControls.add(currPageTxt);
				pageControls.add(totalPagesLbl);
				pageInfo.add(new Label("Page:"));
				pageInfo.add(pageControls);
	
				VerticalPanel pageSizePanel = new VerticalPanel();
				pageSizeTxt.setText("" + pageSize);
				pageSizeTxt.setWidth("60px");
				pageSizePanel.add(new Label("Page size:"));
				pageSizePanel.add(pageSizeTxt);
	
				footer.setWidget(0, 0, first);
				footer.setWidget(0, 1, prev);
				footer.setWidget(0, 2, pageInfo);
				footer.setWidget(0, 3, next);
				footer.setWidget(0, 4, last);
				footer.setWidget(0, 5, pageSizePanel);
			}

			if ((type == DELETE || type == BOTH) && type != READ_ONLY) {
				Button deleteAll = new Button(CPConstants.DELETE_IMAGE+"   <div>Delete selected</div>");
				Button generateReport = new Button(CPConstants.REPORT_IMAGE+"<div>Generate report</div>");
				
				deleteAll.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						ArrayList<String> ids = new ArrayList<String>();
						FixedWidthGrid dataTable = table.getDataTable();
						for (int i = 0; i < dataTable.getRowCount(); i++) {
							CheckBox cb = (CheckBox) dataTable.getWidget(i, 0);
							if (cb.getValue()) {
								ids.add(cb.getName());
							}
						}
						actionEvent = new ActionEvent(ids, ActionEvent.DELETE_ALL);
						actionEvent.setDataBaseTableName(dataBaseTableName);
						fireEvent(actionEvent);
					}
				});
				
				generateReport.addClickHandler(new ClickHandler(){
					@Override
					public void onClick(ClickEvent event) {	
						EditPresenter editPresenter = new EditPresenter(rpcService, handlerManager, new EditView("Report",""), History.getToken());
						editPresenter.go(editPresenter.getDisplay().getWindow());
					}
				});
				
				footer.setWidget(0, 6, deleteAll);
				footer.setWidget(0, 8, generateReport);
				footer.getFlexCellFormatter().setWidth(0, 7, "310px");
			}
			footer.setCellPadding(10);

			if(type != REPORT){
				FixedWidthFlexTable tableFooter = new FixedWidthFlexTable();
				tableFooter.setWidget(0, 0, footer);
				tableFooter.getFlexCellFormatter().setColSpan(0, 0,
						table.getDataTable().getCellCount(0));
				table.setFooterTable(tableFooter);
			}
			else
				table.setFooterTable(new FixedWidthFlexTable());
		}
		currPageTxt.setWidth("40px");
		currPageTxt.setText(Integer.toString(currentPage));
		pageSizeTxt.setText("" + pageSize);
		pageSizeTxt.setWidth("60px");
	}

	private Button getEditButton(final String id) {
		Button edit = new Button(CPConstants.EDIT_IMAGE);
		edit.setTitle("Press to edit "+id);
		edit.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				actionEvent = new ActionEvent(id, ActionEvent.EDIT);
				fireEvent(actionEvent);
			}
		});
		return edit;
	}

	private Button getDeleteButton(final String id) {
		Button del = new Button(CPConstants.DELETE_IMAGE);
		del.setTitle("Press to delete "+id);
		del.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				actionEvent = new ActionEvent(id, ActionEvent.DELETE);
				actionEvent.setDataBaseTableName(dataBaseTableName);
				fireEvent(actionEvent);
			}
		});
		return del;
	}

	private Button getSelectButton(final TableRow tr) {
		Button sel = new Button(CPConstants.SELECT_IMAGE);
		sel.setTitle("Press to select "+tr.getTableData().get(0).getValue());
		sel.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				ListRow lr = new ListRow(new HashMap<String, String>(),
						new ArrayList<String>());
				int i = 0;
				for (TableData td : tr.getTableData()) {
					lr.put(tableRows.getColumns().get(i).getHeader(),
							td.getValue());
					lr.add(td.getValue());
					i++;
				}
				actionEvent = new ActionEvent(lr, ActionEvent.SELECT);
				fireEvent(actionEvent);
			}
		});
		return sel;
	}

	private void init() {
		handlerManager = new HandlerManager(this);

		table.getHeaderTable().addTableListener(new TableListener() {
			@Override
			public void onCellClicked(SourcesTableEvents sender, int row,
					int cell) {
				if (row == 0) {
					FixedWidthFlexTable h = (FixedWidthFlexTable) sender;
					if (cell != h.getCellCount(0) - 1)
						if (type == CustomPagingScrollTable.DELETE
								|| type == CustomPagingScrollTable.BOTH || type == REPORT)
							if (cell != 0) {
								String tableName = h.getHTML(row, cell).replaceFirst("<span>.*", "");
								if (headerCell == cell) {
									if (direction.equals(" ASC"))
										direction = " DESC";
									else
										direction = " ASC";
								} else {
									direction = " ASC";
									headerCell = cell;
								}
								order = "`"
										+ (tableName + "`" + direction);
								fillTable();
							}
				}
			}
		});

		next.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				currentPage += 1;
				if (currentPage > totalPages) {
					currentPage = totalPages;
				}
				doChange();
			}
		});
		prev.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				currentPage -= 1;
				if (currentPage < 1) {
					currentPage = 1;
				}
				doChange();
			}
		});
		last.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				currentPage = totalPages;
				doChange();
			}
		});
		first.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				currentPage = 1;
				doChange();
			}
		});
		currPageTxt.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				try {
					int value = Integer.parseInt(currPageTxt.getText());
					if (value <= totalPages && value > 0) {
						currentPage = value;
						doChange();
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		});
		pageSizeTxt.addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				try {
					int value = Integer.parseInt(pageSizeTxt.getText());
					if (value > 0) {
						pageSize = value;
						currentPage = 1;
						doChange();
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		});
	}

	public void fillTable() {
		ModalOverlay.show();
		rpcService.getTableData(username, objectName, objectType, name, currentPage,
				pageSize, order, filters, new AsyncCallback<TableRows>() {

					@Override
					public void onSuccess(TableRows result) {
						// DataBaseTableName = result.get
						table.getDataTable().clearAll();
						tableRows = result;
						if (tableRows.getRows().size() > 0) {
							totalPages = (int) Math.ceil((double) tableRows
									.getTotal() / (double) pageSize);

							if (type != READ_ONLY && type != REPORT) {
								if (type == DELETE || type == BOTH) {
									table.getDataTable().resize(
											tableRows.getRows().size(),
											tableRows.getRows().get(0)
													.getTableData().size() + 2);
								} else
									table.getDataTable().resize(
											tableRows.getRows().size(),
											tableRows.getRows().get(0)
													.getTableData().size() + 1);
							}
							else
								table.getDataTable().resize(tableRows.getRows().size(),tableRows.getRows().get(0).getTableData().size());
							
							HashMap<TableColumn, Integer> columnWidths = new HashMap<TableColumn, Integer>();
							for (int i = 0; i < tableRows.getRows().size(); i++) {
								for (int j = 0; j < tableRows.getRows().get(i).getTableData().size(); j++) {
									if (!tableRows.getRows().get(i).getTableData().get(j).isVisible()) {
										table.getDataTable().getCellFormatter().setVisible(i,(type==DELETE||type==BOTH)?j+1:j,false);
									}
									setCellAlignment(i, j);
									table.getDataTable().setText(i,(type==DELETE||type==BOTH)?j+1:j,tableRows.getRows().get(i).getTableData().get(j).getValue());
								}
								if(type!=READ_ONLY&&type!=REPORT)
								addControls(i,tableRows.getRows().get(i).getTableData().get(tableRows.getPrimary()),tableRows.getRows().get(i));
							}
							
							int p = 0;
							for (TableColumn tc : tableRows.getColumns()) {
								int col = tableRows.getColumns().indexOf(tc);
								if (!tc.isVisible()) {
									p++;
									table.getHeaderTable()
											.getCellFormatter()
											.setVisible(
													0,
													(type == DELETE || type == BOTH) ? col + 1
															: col, false);
								} else {
									if (tc.getWidth() > 0)
										table.setMaximumColumnWidth((type==DELETE||type==BOTH)?col+1-p:col-p, tc.getWidth());
									
									columnWidths.put(tc, (type==DELETE||type==BOTH)?col+1-p:col-p);
								}
							}
							
							for (int i = table.getDataTable().getCellCount(0) - 1; i > table
									.getDataTable().getCellCount(0) - 1 - p; i--) {
								table.setMaximumColumnWidth(i, 0);
							}
							if (type == EDIT || type == DELETE || type == BOTH || type == SELECT)
								table.setMaximumColumnWidth(table.getDataTable().getCellCount(0)- p - 1, 120);
							
							addHeaderAndFooter();
							table.fillWidth();
							
							for(TableColumn tc : tableRows.getColumns())
								if(tc.isVisible())
									tc.setWidth(table.getColumnWidth(columnWidths.get(tc)));
							addFilters();
							
							dataBaseTableName = result.getTableName();
							errorMessage.setText("");
						} else{
							errorMessage.setText("There are no results");
						}
						ModalOverlay.hide();
					}

					@Override
					public void onFailure(Throwable caught) {
						ModalOverlay.hide();
						MessageBox.error("Error",
								"An error occured while populating the list");
					}
				});
	}

	private void setCellAlignment(int row, int col) {
		switch (tableRows.getRows().get(row).getTableData().get(col)
				.getAlignment()) {
		case 0:
			table.getDataTable()
					.getCellFormatter()
					.setHorizontalAlignment(row,
							(type == DELETE || type == BOTH) ? col + 1 : col,
							HasHorizontalAlignment.ALIGN_LEFT);
			break;
		case 1:
			table.getDataTable()
					.getCellFormatter()
					.setHorizontalAlignment(row,
							(type == DELETE || type == BOTH) ? col + 1 : col,
							HasHorizontalAlignment.ALIGN_CENTER);
			break;
		case 2:
			table.getDataTable()
					.getCellFormatter()
					.setHorizontalAlignment(row,
							(type == DELETE || type == BOTH) ? col + 1 : col,
							HasHorizontalAlignment.ALIGN_RIGHT);
			break;
		default:
			table.getDataTable()
					.getCellFormatter()
					.setHorizontalAlignment(row,
							(type == DELETE || type == BOTH) ? col + 1 : col,
							HasHorizontalAlignment.ALIGN_DEFAULT);
			break;
		}
	}

	private void doChange() {
		fillTable();
	}
}
